package main;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Vector;

public class ChiefScientistAssistant implements Runnable {

	private boolean experimentsFinished;
	
	private ArrayList<Expirement> experiments;
	private Repository repository;
	private Vector<HeadOfLaboratory> labHeads;
	private ScienceStore store;
	private Statistics stats;
	private ChiefScientist chief;
	
	public ChiefScientistAssistant(ArrayList<Expirement> experiments,Repository repository, 
			Vector<HeadOfLaboratory> labHeads, ScienceStore store, Statistics stats, ChiefScientist chief)
	{
		this.experiments=experiments;
		this.repository=repository;
		this.labHeads=labHeads;
		this.store=store;
		this.stats = stats;
		this.chief=chief;
		experimentsFinished=false;
	}

	
	public synchronized void run() {
		buyInitialEquipment();
		while(!experimentsFinished)
		{
			System.out.println("Chief Assitant check experiments");
			synchronized(experiments){
				startExperiments();
				try{
					experiments.wait();
				}catch(InterruptedException e)
				{
				}
				checkExperimentsStatus();
			}
		}	
		finish();
	}

	/**
	 * running if all experiments finish and end program gracefully
	 */
	private void finish() {
		System.out.println("Chief Assitant- ALL EXPERIMENTS FINISHED - shuting down executers  in labs");
		shutdownLabs();
		//Print Statistics
		System.out.println(stats.toString());
		System.out.println("Chief Assitant FINISHED - calling for Chief");
		notifyAll();
		
	}

	/**
	 * shutdown all labs to end program gracefully
	 */
	private void shutdownLabs() {
		synchronized(labHeads)
		{
			Iterator<HeadOfLaboratory> i=labHeads.iterator();
			while(i.hasNext())
			{
				i.next().shutdown();
			}
		}
		
	}

	/**
	 * buy from store the minimum equipment needed to run all experiments
	 */
	private void buyInitialEquipment() {
		
		HashMap<String,Integer> shopingList=pickInitialEquipment();
		buyNeededEquipment(shopingList);
	}

	/**
	 * buy from store the minimum labs needed to run all experiments
	 */
	private void buyInitialLabs() {
		
		Iterator<Expirement> i=experiments.iterator();
		while(i.hasNext())
		{
			Expirement exp=i.next();
			assignLaboratoryForExperiment(exp);
		}	
		
	}
	
	/**
	 * pick the minimum equipment needed to run all experiments. 
	 * for each equipment, will check the maximum amount an experiment in our experimentsList need, and enter it into a shopingList.
	 * @return shoping list with required equipment
	 * 
	 */
	private HashMap<String,Integer> pickInitialEquipment()
	{
		HashMap<String,Integer> shopingList=new HashMap<String,Integer>();
		Iterator<Expirement> expIter=experiments.iterator();
		while(expIter.hasNext())
		{
			Expirement exp=expIter.next();
			HashMap<String,Integer> expReqEquipment=exp.getReqEquipment();
			Iterator<String> itemIter=expReqEquipment.keySet().iterator();
			while(itemIter.hasNext())
			{
				String name=itemIter.next();
				int itemFromExpAmount=expReqEquipment.get(name).intValue();
				if(shopingList.containsKey(name))
				{
					int itemPresentAmount=shopingList.get(name).intValue();
					if(itemFromExpAmount>itemPresentAmount)
					{
						shopingList.put(name, itemFromExpAmount);
					}
				}
				else
				{
					shopingList.put(name, itemFromExpAmount);
				}
			}
		}
		return shopingList;
	}

	/**
	 * check the status of experiments after being probed from the cheif scientist, to look if anyone need equipment 
	 */
	private synchronized void checkExperimentsStatus() {
		Iterator<Expirement> i=experiments.iterator();
		experimentsFinished=true;
		while(i.hasNext())
		{
			Expirement exp=i.next();
			if(exp.getStatus()==4)		//NEEDEDEQUIPMENT
			{	
				System.out.println("chiefAssitant go to buy equipment for exp "+exp.getID());
				buyNeededEquipment(exp.getReqEquipment());
			}	
			if(exp.getStatus()!=3)		//COMPLETE
				experimentsFinished=false;
		}	
	}

	/**
	 * check and try to run experiments from experimentsList.
	 * if possible to run experiment,will create runnableexperiment and send it to the assigned head of laboratory
	 */
	private void startExperiments() {
		Iterator<Expirement> i=experiments.iterator();
		while(i.hasNext())
		{
			Expirement exp=i.next();
			if(exp.getStatus()==0)		//INCOMPLETE
			{
				if(exp.hasPrerequirementsMet())
				{
					HeadOfLaboratory expLaboratory;
					expLaboratory=assignLaboratoryForExperiment(exp);
					RunnableExperiment runExp=new RunnableExperiment(exp, repository, expLaboratory, chief);
					expLaboratory.runExpirement(runExp);
				}
			}
		}	
	}

	/**
	 * assign a laboratory to experiment based on experiment specialization. if such laboratory doesnt exist in company, will buy one.
	 * @param exp - the experiment wanted to run
	 * @return the laboratory assigned to that experiment
	 */
	private HeadOfLaboratory assignLaboratoryForExperiment(Expirement exp) {
		HeadOfLaboratory assignedLab=null;
		Iterator<HeadOfLaboratory> i=labHeads.iterator();
		String expSpec=exp.getSpec();
		while(i.hasNext() && assignedLab==null)
		{
			assignedLab=i.next();
			if(!assignedLab.getSpec().equals(expSpec))
			{
				assignedLab=null;
			}
		}
		if(assignedLab==null)
		{
			assignedLab=buyNeededLab(expSpec);
			labHeads.add(assignedLab);
		}
		return assignedLab;
	}

	/**
	 * buy the needed lab from store
	 * @param spec - the lab specialization
	 * @return needed lab head
	 */
	private HeadOfLaboratory buyNeededLab(String spec) {
		Lab lab=store.buyLab(spec, stats);
		HeadOfLaboratory labHead=lab.getLabHead();
		return labHead;
	}
	
	/**
	 * buy equipment needed from store according to given shoping list (neededEquipmentList)
	 * @param neededEquipmentList - a list of needed equipment contain name and amount
	 */
	private void buyNeededEquipment(HashMap<String, Integer> neededEquipmentList) {
		HashMap<String,Integer> shopingList;
		shopingList=repository.checkAvailability(neededEquipmentList);
		if(shopingList!=null)		//there is shortage in equipment
		{
			Iterator<String> i=shopingList.keySet().iterator();
			while(i.hasNext())
			{
				String name=i.next();
				int amount=shopingList.get(name).intValue();
				System.out.println("equipment "+name+" is needed - "+amount+" units. going to store to buy");
				int bought=store.buyEquipment(name, amount, stats);		//not always will be able to buy everything! store can also be depleted
				System.out.println("equipment "+name+" was bought - "+bought+" units. add to repository");
				repository.addEquipment(name, bought);
			}
		}
		
	}

	private void buyNeededEquipment(Expirement exp) {
		HashMap<String,Integer> shopingList;
		shopingList=repository.checkAvailability(exp.getReqEquipment());
		if(shopingList!=null)		//there is shortage in equipment
		{
			Iterator<String> i=shopingList.keySet().iterator();
			while(i.hasNext())
			{
				String name=i.next();
				int amount=shopingList.get(name).intValue();
				int bought=store.buyEquipment(name, amount, stats);		//not always will be able to buy everything! store can also be depleted
				repository.addEquipment(name, bought);
			}
		}
		
		
	}

	/**
	 * 
	 * @return true if all experiments finished
	 */
	public boolean isExperimentsFinished() {
		
		return experimentsFinished;
	}

}